package hexagon.security.core.domain;

import hexagon.security.core.CodeIsExistedException;
import hexagon.security.core.CorrelationException;
import hexagon.security.core.NameIsExistedException;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.Validate;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * @author XueguiYuan
 * @version 1.0 (created time: 2016/12/3).
 */
@NoArgsConstructor
@Getter
@Setter
@ToString(of = {"name", "code", "description"})
@Entity
@Table(name = "security_resource")
public class SecurityResource extends SecurityAbstractEntity {

    @NotNull
    private String name;

    @NotNull
    private String code;

    private SecurityResourceType type;

    private String description;

    private String icon;

    private int level;

    private int sort;

    private String url;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id")
    private List<SecurityResource> children;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "security_resource_id")
    private List<ResourceAssignment> resourceAssignments;

    public static final Finder<Long, SecurityResource> find = new Finder<>(SecurityResource.class);

    @Override
    public String[] businessKeys() {
        return new String[]{"name", "code"};
    }

    public void addChild(SecurityResource child) {
        child.setLevel(level + 1);
        child.save();
        this.children.add(child);
        this.save();
    }

    public void removeChild(SecurityResource child) {
        children.remove(child);
        child.delete();
    }


    private void removeChildren() {
        for (SecurityResource child : children) {
            child.delete();
        }
    }

    @Override
    public boolean delete() {
        if (!ResourceAssignment.findBySecurityResource(this).isEmpty()) {
            throw new CorrelationException("securityResource has authority, cannot remove it.");
        }
        removeChildren();
        return super.delete();
    }

    public static void batchSave(List<? extends SecurityResource> securityResources) {
        for (SecurityResource securityResource : securityResources) {
            securityResource.save();
        }
    }

    public SecurityResource findByName(String name) {
        return find.where()
                .eq("name", name)
                .setMaxRows(1)
                .findUnique();
    }

    public void changeName(String name) {
        checkName(name);
        if (!name.equals(this.name)) {
            isNameExisted(name);
            this.name = name;
            this.save();
        }
    }

    protected void isNameExisted(String name) {
        if (findByName(name) != null) {
            throw new NameIsExistedException("securityResource name existed.");
        }
    }

    public SecurityResource findByCode(String code) {
        return find.where()
                .eq("code", code)
                .setMaxRows(1)
                .findUnique();
    }

    public void changeCode(String code) {
        checkCode(code);
        if (!code.equals(this.code)) {
            isCodeExisted(code);
            this.code = code;
            this.save();
        }
    }

    protected void isCodeExisted(String code) {
        if (findByCode(code) != null) {
            throw new CodeIsExistedException("securityResource code existed.");
        }
    }

    private void checkName(String name) {
        Validate.notBlank(name, "name cannot be empty.");
    }

    private void checkCode(String code) {
        Validate.notBlank(code, "code cannot be empty.");
    }

    public static enum SecurityResourceType {
        MENU, URL, PAGE_ELEMENT
    }
}
